package com.divillysausages.dsair 
{
	import com.divillysausages.dsair.input.InputManager;
	import com.divillysausages.dsair.invoke.InvokeManager;
	import com.divillysausages.dsair.log.LogManager;
	import com.divillysausages.dsair.resource.ResourceManager;
	import com.divillysausages.dsair.throttle.ThrottleManager;
	import com.divillysausages.dsair.windows.Alert;
	import flash.desktop.NativeApplication;
	import flash.display.Screen;
	import flash.display.Sprite;
	import flash.display.Stage;
	import flash.display.StageAlign;
	import flash.display.StageScaleMode;
	import flash.events.ErrorEvent;
	import flash.events.UncaughtErrorEvent;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.system.Capabilities;
	import flash.system.Security;
	
	/**
	 * The main DS Air class that controls the engine
	 * @author Damian Connolly
	 */
	public class DSAir 
	{
		/********************************************************************/
		
		private static var m_stage:Stage 			= null; // the stage for the app
		private static var m_main:Sprite			= null; // the main class
		private static var m_started:Boolean		= false;// is the engine started?
		private static var m_appName:String			= null; // the name of the app
		private static var m_appVersion:String		= null; // the version of the app
		private static var m_screenRes:Point		= null; // the screen resolution
		private static var m_mainScreenSize:Point	= null; // the main screen usable size
		private static var m_errorAlert:Alert		= null; // the error alert popup
		
		/********************************************************************/
		
		/**
		 * The stage for the app
		 */
		public static function get stage():Stage { return DSAir.m_stage; }
		
		/**
		 * The main class for the app
		 */
		public static function get main():Sprite { return DSAir.m_main; }
		
		/**
		 * Gets the name of the app
		 */
		public static function get appName():String
		{
			// read the application xml file to get the app name
			if ( DSAir.m_appName == null )
			{
				var appXML:XML		= NativeApplication.nativeApplication.applicationDescriptor;
				var ns:Namespace	= appXML.namespace();
				DSAir.m_appName		= appXML.ns::name;
			}
			return DSAir.m_appName;
		}
		
		/**
		 * Gets the version of the app
		 */
		public static function get appVersion():String
		{
			// read the application xml file to get the app name and version
			if ( DSAir.m_appVersion == null )
			{
				var appXML:XML 		= NativeApplication.nativeApplication.applicationDescriptor;
				var ns:Namespace	= appXML.namespace();
				DSAir.m_appVersion	= ( appXML.ns::versionNumber != undefined ) ? appXML.ns::versionNumber : appXML.ns::version;
			}
			return DSAir.m_appVersion;
		}
		
		/**
		 * Returns the resolution of the screen
		 */
		public static function get screenRes():Point { return DSAir.m_screenRes; }
		
		/**
		 * Returns the main screen usuable size (without the dock)
		 */
		public static function get mainScreenSize():Point { return DSAir.m_mainScreenSize; }
		
		/**
		 * Returns the log manager that we're using for the app
		 */
		public static function get logManager():LogManager { return LogManager.instance; }
		
		/********************************************************************/
		
		/**
		 * Inits the framework
		 * @param main The main class
		 */
		public static function init( main:Sprite ):void
		{
			// we're started
			DSAir.m_started = true;
			
			// hold the main class and the stage
			DSAir.m_main 	= main;
			DSAir.m_stage	= main.stage;
			
			// set the scale mode
			DSAir.m_stage.scaleMode = StageScaleMode.NO_SCALE;
			DSAir.m_stage.align		= StageAlign.TOP_LEFT;
			
			// do our initial trace
			DSAir.log( DSAir, "App: " + DSAir.appName + " (v" + DSAir.appVersion + ")" );
			DSAir.log( DSAir, "FP: " + Capabilities.version + ", security sandbox: " + Security.sandboxType );
			
			// init the invoke manager so that we get any files that were
			// passed when the app was started
			InvokeManager.instance;
			
			// enable throttling
			ThrottleManager.instance.active = true;
			
			// init the resource manager
			ResourceManager.instance;
			
			// add an uncaught exceptions handler
			DSAir.m_main.loaderInfo.uncaughtErrorEvents.addEventListener( UncaughtErrorEvent.UNCAUGHT_ERROR, DSAir._onUncaughtError );
			
			// get the screen resolution
			DSAir.m_screenRes = new Point( Capabilities.screenResolutionX, Capabilities.screenResolutionY );
			
			// main screen usable size
			var rect:Rectangle		= Screen.mainScreen.visibleBounds;
			DSAir.m_mainScreenSize 	= new Point( rect.width, rect.height );
		}
		
		/**
		 * Logs a debug message to the output and logger
		 * @param reporter The reporting object
		 * @param message The message to log
		 */
		public static function debug( reporter:*, message:String ):void
		{
			if ( DSAir._check() )
				LogManager.instance.debug( reporter, message );
		}
		
		/**
		 * Logs a message to the output and logger
		 * @param reporter The reporting object
		 * @param message The message to log
		 */
		public static function log( reporter:*, message:String ):void
		{
			if( DSAir._check() )
				LogManager.instance.log( reporter, message );
		}
		
		/**
		 * Logs a warning message to the output and logger
		 * @param reporter The reporting object
		 * @param message The message to log
		 */
		public static function warn( reporter:*, message:String ):void
		{
			if( DSAir._check() )
				LogManager.instance.warn( reporter, message );
		}
		
		/**
		 * Logs an error message to the output and logger
		 * @param reporter The reporting object
		 * @param message The message to log
		 * @param showAlert Should we show an alert window for this error?
		 */
		public static function error( reporter:*, message:String, showAlert:Boolean = true ):void
		{
			if ( !DSAir._check() )
				return;
				
			// log it
			LogManager.instance.error( reporter, message );
			
			// error it
			if ( showAlert )
			{
				// create the alert if needed
				if ( DSAir.m_errorAlert == null )
				{
					DSAir.m_errorAlert 					= new Alert( "Error", "An error occurred" );
					DSAir.m_errorAlert.destroyOnClose	= false; // keep this error window for future use
					DSAir.m_errorAlert.addButton( "Close" );
				}
				
				// set the message
				DSAir.m_errorAlert.msg = reporter + ": " + message;
				DSAir.m_errorAlert.show();
			}
		}
		
		/**
		 * Binds a callback to a key press
		 * @param key The key we're looking to bind
		 * @param callback The callback to call
		 */
		public static function bindKey( key:uint, callback:Function ):void
		{
			if( DSAir._check() )
				InputManager.instance.bind( key, callback );
		}
		
		/**
		 * Unbinds a callback from a key press
		 * @param key The key we're looking to unbind
		 */
		public static function unbindKey( key:uint ):void
		{
			if( DSAir._check() )
				InputManager.instance.unbind( key );
		}
		
		/********************************************************************/
		
		// checks to see if we're good to go
		private static function _check():Boolean
		{
			if ( !DSAir.m_started )
				trace( "Call DSAir.start() before using any of the functions!" );
			return DSAir.m_started;
		}
        
		// called when an uncaught exception occurs
        private static function _onUncaughtError( e:UncaughtErrorEvent ):void
        {
            if ( e.error is Error)
            {
                var error:Error = e.error as Error;
				DSAir.error( DSAir, "Uncaught error: " + error.errorID + ": " + error.message );
            }
            else if ( e.error is ErrorEvent)
            {
                var errorEvent:ErrorEvent = e.error as ErrorEvent;
				DSAir.error( DSAir, "Uncaught error: " + errorEvent.errorID + ": " + errorEvent.text );
            }
            else
            {
				DSAir.error( DSAir, "Uncaught error: " + e.errorID + ": " + e.toString() );
            }
        }
		
	}

}